﻿#include "pch.h"
#include "qgp/qgach.h"
#include "qgp/qgadpt.h"
#include "qgsmm_math.inl"
#include "qgsmm_ldr.h"

namespace _smm
{
	struct Arpr520
	{
		kuint				hdr;
		kushort				bin;
		kushort				ver;

		char				name[64];
		kushort				type;

		kushort				nres;
		kushort				nfrm;
		kushort				ndmm;
		kushort				nbon;
		kushort				nmsh;

		kint				pivot;
	};

	struct Res520
	{
		char				name[64];

		tpl::Color			diff;
		tpl::Color			ambi;
		tpl::Color			spec;
		tpl::Color			emis;
		float				shine;
	};

	struct Tex520
	{
		unsigned int		comp;		// 압축 크기
		unsigned int		real;		// 실제 크기

		// 텍스쳐 크기=sizeof(binImpTex)+압축크기
	};

	struct Frame520
	{
		char				name[64];

		unsigned short		type;		// 프레임 형식
		unsigned short		id;
		unsigned short		pid;
		unsigned short		eid;		// 뼈대 또는 리소스 아이디

		int					flags;

		int					align_1;

		tpl::Mat4			mtm;
		tpl::Mat4			msub;

		tpl::Vec3			loc;
		tpl::Quat			rot;
		tpl::Vec3			scl;

		tpl::Vec3			bnd_min;
		tpl::Vec3			bnd_max;
		tpl::Vec3			bnd_ctr;
		float				bnd_rad;

		float				len;
	};

	struct Mesh520
	{
		int					verts;
		int					faces[3];	// 0=no lod, 1=lod1, 2=lod2
		int					shds;
	};

	struct Unit520
	{
		char				name[64];

		kushort				type;
		kushort				frms;

		float				start;
		float				end;

		int					pivot;
		int					cmpr;

		int					reserved[2];
	};

	struct Anim520
	{
		char				name[64];

		int					id;
		int					pid;
		int					bid;

		float				len;

		int					locs;
		int					rots;
		int					scls;
	};

	//////////////////////////////////////////////////////////////////////////

	enum BinGroup
	{
		BgEnd = 0xFF,

		BgSmm = 21,
		BgSma = 22,
	};

	enum BinRole
	{
		// 리소스
		BreRes = 0,

		// 텍스쳐
		BreTex0 = 10,
		BreTex1 = 11,
		BreTex2 = 12,
		BreTex3 = 13,
		BreTex4 = 14,

		// 내장 텍스쳐
		BreImpTex0 = 20,
		BreImpTex1 = 21,
		BreImpTex2 = 22,
		BreImpTex3 = 23,
		BreImpTex4 = 24,

		// 프레임
		BreFrame = 50,
		BreMesh = 51,

		// 메시
		BreIndex = 61,
		BreVertex = 62,
		BreNormal = 63,
		BreTexCoord = 64,
		BreDiffuse = 65,
		BreBinormal = 66,
		BreTangent = 67,
		BreWeight = 68,

		// 애니메이션
		BreUnit = 0,
		BreAnim = 1,

		// 값
		BreALoc = 11,
		BreARot = 12,
		BreAScl = 13,

		// 오류
		BreNone = 0xFF,
	};

	struct BinSec
	{
		unsigned char		grp;
		unsigned char		sec;

		unsigned short		size;
		int					count;
	};
}

using namespace _smm;

#define _CD(t)			((const t*)data)		// CAST DATA
#define _MD(t)			(data += sizeof(t))		// MOVE DATA
#define _MD_N(n)		(data += n)				// MOVE DATA + byte
#define _MD_TN(t,n)		(data += sizeof(t) * n)	// MOVE DATA + sizeof() * n

//
bool qgSmmCreator::_PrivateRead520(const kbyte* data, kint size, kint flags, const char* filename)
{
	//
	const Arpr520* hdr = _CD(Arpr520);
	if (hdr->hdr != K_MAKEFOURCC('Q', 'C', 'L', '5') ||
		hdr->bin != K_MAKEWORD('M', 'M') ||
		hdr->ver != K_MAKEWORD(0x20, 0x05) ||
		hdr->nres == 0 ||
		hdr->nfrm == 0)
		return false;
	_MD(Arpr520);

	//
	qgRdh* rdh = qgRdh::Instance;

	//
	char basedir[1024];
	char* basepos;
	k_divpath(filename, basedir, 1024 - 1, NULL, 0);
	basepos = *basedir ? basedir + k_strlen(basedir) : basedir;

	//
	SetName(hdr->name);

	//
	bool isok = false;
	const BinSec* sec;
	BinRole prole = BreNone;

	int resid = -1;
	qgSmmFrame* frm = NULL;

	const Frame520* bfm;
	const Mesh520* bms;

	//
	for (;;)
	{
		sec = _CD(BinSec);
		_MD(BinSec);

		int rsize = sec->count * sec->size;

		if (sec->grp == BgSmm)
		{
			switch (sec->sec)
			{
				case BreRes:
					resid++;
					prole = BreRes;
					break;

				case BreTex0:
				case BreTex1:
				case BreTex2:
				case BreTex3:
				case BreTex4:
					if (prole == BreRes)
					{
						k_strncpy(basepos, (const char*)data, sec->count);
						if (sec->sec == BreTex0)
						{
							qgTexture* tex = rdh->CreateTexture(basedir, 0);
							_texs.push_back(tex);
							_mtfs.push_back(K_BIT(0));
							kobj_unload(tex);
						}
						else if (sec->sec == BreTex1)
						{
							int& n = *_mtfs.rbegin();
							n |= K_BIT(1);
						}
					}
					break;

				case BreFrame:
					bfm = _CD(Frame520);

					if (bfm->type == QGSMMFRM_DUMMY)
					{
						qgSmmFrame* p = kobj_create(qgSmmFrame);
						p->SetName(bfm->name);
						p->SetId(bfm->id);
						p->SetPid(bfm->pid > _frms.size() ? 0 : bfm->pid);

						_dmms.push_back(p);
						frm = p;
					}
					else if (bfm->type == QGSMMFRM_BONE)
					{
						qgSmmBone* p;

						if (bfm->id == 0)
							p = this;
						else
						{
							p = kobj_create(qgSmmBone);
							p->SetName(bfm->name);
							p->SetId(bfm->id);
							p->SetPid(bfm->pid > _frms.size() ? 0 : bfm->pid);
						}

						p->SetBoneId(bfm->eid);
						p->SetSubTM(bfm->msub);
						p->SetCombineTM(bfm->msub);

						_bons.push_back(p);
						frm = p;
					}
					else if (bfm->type == QGSMMFRM_MESH)
					{
						qgSmmMesh* p;

						if (!K_OFLAG(bfm->flags, 0x02))
							p = kobj_create(qgSmmMesh);
						else
						{
							qgSmmSkin* s = kobj_create(qgSmmSkin);
							p = s;

							s->_cmbns = hdr->nbon;
							s->_pcmbn = k_new(hdr->nbon, tpl::Mat4);
							s->_pinds = k_new(hdr->nbon, tpl::Mat4*);

							s->_insts |= 0x8000;
						}

						p->SetResId(bfm->eid);
						p->SetName(bfm->name);
						p->SetId(bfm->id);
						p->SetPid(bfm->pid > _frms.size() ? 0 : bfm->pid);

						_mshs.push_back(p);
						frm = p;
					}
					else
					{
						k_assert(false, "unknown frame type!");
						return false;
					}

					//
					frm->_calc = bfm->mtm;
					frm->_local = bfm->mtm;

					frm->_defm = bfm->mtm;
					frm->_len = bfm->len;

					frm->_trfm.loc = frm->_orgm.loc = bfm->loc;
					frm->_trfm.rot = frm->_orgm.rot = bfm->rot;
					frm->_trfm.scl = frm->_orgm.scl = bfm->scl;

					frm->_bmin = bfm->bnd_min;
					frm->_bmax = bfm->bnd_max;
					frm->_bctr = bfm->bnd_ctr;
					frm->_brad = bfm->bnd_rad;

					//
					_frms.push_back(frm);
					prole = BreFrame;

					//
					frm->_link.parent = _frms[frm->_pid];

					break;

				case BreMesh:
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH)
					{
						bms = _CD(Mesh520);

						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->faces = bms->faces[0];
						dd->indexes = dd->faces * 3;
						dd->vertices = bms->verts;
					}
					else
					{
						k_traces(KLV_FATAL, "smm", "invalid mesh desc");
					}
					break;

				case BreIndex:
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(int))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_i.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_i.push_back(((kint*)data)[i]);
					}
					else
					{
						k_traces(KLV_FATAL, "smm", "invalid mesh desc");
					}
					break;

				case BreVertex:	// 정점 위치
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(tpl::Vec3))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_v.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_v.push_back(((tpl::Vec3*)data)[i]);
					}
					else
					{
						// 훔
						k_traces(KLV_FATAL, "smm", "invalid mesh vertex data found.");
					}
					break;

				case BreNormal:	// 법선
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(tpl::Vec3))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_n.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_n.push_back(((tpl::Vec3*)data)[i]);
					}
					else
					{
						// 훔
						k_traces(KLV_FATAL, "smm", "invalid mesh normal data found.");
					}
					break;

				case BreTexCoord:	// 텍스쳐
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(tpl::Vec2))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_t.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_t.push_back(((tpl::Vec2*)data)[i]);
					}
					else
					{
						// 훔
						k_traces(KLV_FATAL, "smm", "invalid mesh texcoord data found.");
					}
					break;

				case BreDiffuse:	// 색깔
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(kuint))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_d.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_d.push_back(((kuint*)data)[i]);
					}
					else
					{
						// 훔
						k_traces(KLV_FATAL, "smm", "invalid mesh diffuse data found.");
					}
					break;

				case BreBinormal:	// 종법선
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(tpl::Vec3))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_b.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_b.push_back(((tpl::Vec3*)data)[i]);
					}
					else
					{
						// 훔
						k_tracef(KLV_FATAL, "smm", "invalid mesh binormal data found.");
					}
					break;

				case BreTangent:	// 탄젠트
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(tpl::Vec3))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_a.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_a.push_back(((tpl::Vec3*)data)[i]);
					}
					else
					{
						// 훔
						k_tracef(KLV_FATAL, "smm", "invalid mesh tangent data found.");
					}
					break;

				case BreWeight:	// 웨이트
					if (prole == BreFrame && frm->GetFrameType() == QGSMMFRM_MESH && sec->size == sizeof(qbVertexWeight))
					{
						qgSmmMesh* p = (qgSmmMesh*)frm;
						qbSmmDrawData* dd = p->_data;

						dd->_w.reserve(sec->count);

						for (int i = 0; i < sec->count; i++)
							dd->_w.push_back(((qbVertexWeight*)data)[i]);
					}
					else
					{
						// 훔
						k_tracef(KLV_FATAL, "smm", "invalid mesh skin influence data found.");
					}
					break;

				default:			// 뭐짐
					k_tracef(KLV_FATAL, "smm", "unknown binary section '%d'(size=%d, count=%d)", sec->sec, sec->size, sec->count);
					break;
			}
		}
		else if (sec->grp == BgEnd)
		{
			isok = true;
			break;
		}
		else
		{
			// 이건 뭐지...
			k_tracef(KLV_FATAL, "smm", "unknown binary group '%d:%d'(size=%d, count=%d)", sec->grp, sec->sec, sec->size, sec->count);
			break;
		}

		//
		_MD_N(rsize);
	}

	//
	if (!isok)
		return false;

	//
	if (hdr->pivot > 0)
		_pivot = _frms[hdr->pivot];

	kForEach(qgSmmMesh* m, _mshs)
		m->_GenerateMesh(this);

	//
	_link.parent = NULL;

	return true;
}

